package net.ex337.hotrepart.loadtester.executors;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import net.ex337.hotrepart.loadtester.telemetry.GeneratedLoad;
import net.ex337.hotrepart.loadtester.telemetry.LoadGenerator;
import net.ex337.hotrepart.loadtester.telemetry.LoadRecorder;
import net.ex337.hotrepart.loadtester.telemetry.LoadRecorderDelegate;

/**
 *
 * A thread pool-based executor that records the load generated by the
 * items it runs. This load is then tallied at a regular interval.
 *
 * @author ian
 */
public final class LoadGeneratingThreadPoolExecutor extends ThreadPoolExecutor implements LoadRecorder {

    /*
     * The load recording logic, delegated to below.
     */
	private LoadRecorder loadRecorderDelegate;

    /**
     *
     * @param type The "type" of load, so far one of "reads", "writes" or "updates".
     */
	public LoadGeneratingThreadPoolExecutor(String type, int initThreads, int maxThreads) {
		super(initThreads, maxThreads, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
		this.loadRecorderDelegate = new LoadRecorderDelegate(type);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	protected void afterExecute(Runnable r, Throwable t) {

		try {
			((FutureTask<Runnable>) r).get();
		} catch (ExecutionException e) {
			addThrowable(e.getCause());
		} catch (InterruptedException e) {
			addThrowable(e);
		}		
		super.afterExecute(r, t);
	}

    /**
     * Work packages are added to this pool via a LoadRecorder method
     * to allow decoration first, and to unify the method across
     * different types of thread pools.
     *
     * @param load the LoadGenerator to run
     */
    public void addLoadGenerator(LoadGenerator load) {
		load.setLoadReceiver(this);
		super.submit(load);
	}

    /*
     * all delegate methods below.
     */

	public void addThrowable(Throwable t) {
		loadRecorderDelegate.addThrowable(t);
	}

    public void addThrowables(List<Throwable> e) {
        loadRecorderDelegate.addThrowables(e);
    }

	public void addGeneratedLoad(GeneratedLoad load) {
		loadRecorderDelegate.addGeneratedLoad(load);
	}

	public GeneratedLoad getGeneratedLoadToDate() {
		return loadRecorderDelegate.getGeneratedLoadToDate();
	}

	public List<Throwable> getThrowablesToDate() {
		return loadRecorderDelegate.getThrowablesToDate();
	}


}

